/*
 * Copyright 2008 Mark Davidson.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.doofer

import com.documentum.com.*
import com.documentum.fc.common.*
import com.documentum.fc.client.*

/**
 * Provides convenience methods for simplified session handling.
 *
 * @author Mark Davidson
 */
class SessionUtil {

    def manager         // IDfSessionManager instance
    def client          // IDfClient instance
    def clientx         // IDfClientX instance
    def username        // username
    def password        // password
    def repository      // name of repository/docbase

    /**
     * Constructor - takes a Map of connection information as a
     * parameter. The named parameters are:
     *
     * <ul>
     * <li>username - the username to connect as</li>
     * <li>password - the password</li>
     * <li>repository - the repository/docbase to connect to</li>
     * </ul>
     *
     * @param params The named parameters. See description for more info.
     */
    SessionUtil(Map params) {
        clientx = new DfClientX()
        client = clientx.localClient
        manager = client.newSessionManager()

        username = params.username
        password = params.password
        repository = params.repository
    }

    /**
     * Constructs an IDfLoginInfo object from a username and
     * password. The named parameters are:
     *
     * <ul>
     * <li>username - the username to connect as</li>
     * <li>password - the password</li>
     * </ul>
     *
     * @param params The named parameters. See description for more info.
     * @return The login info
     */
    def newIdentity(Map params) {
        def identity = clientx.loginInfo
        identity.user = params.username
        identity.password = params.password
        identity
    }

    /**
     * Opens, uses (via callback to closure) and close a
     * repository session. The named parameters are:
     *
     * <ul>
     * <li>asUser - the user to impersonate (optional)</li>
     * </ul>
     *
     * @param params The named parameters. See description for more info.
     * @param closure The closure (param = session)
     */
    void useSession(Map params = null, Closure closure) {
        def session
        try {
            session = connect(params)
            closure(session)
        } finally {
            disconnect(session: session)
        }
    }

    /**
     * Opens, uses (via callback to closure) and close a
     * repository session. The closure context is substituted
     * for the session context so that calls to IDfSession methods
     * do not require the object name to be specified. e.g.
     * <pre>
     * new SessionUtil().withSession { println docbaseName }
     * </pre>
     * The named parameters are:
     *
     * <ul>
     * <li>asUser - the user to impersonate (optional)</li>
     * </ul>
     *
     * @param params The named parameters. See description for more info.
     * @param closure The closure (param = session)
     */
    void withSession(Map params = null, Closure closure) {
        def session
        try {
            session = connect(params)
            session.with(closure)
        } finally {
            disconnect(session: session)
        }
    }

    /**
     * Opens a repository session. The named parameters are:
     *
     * <ul>
     * <li>asUser - the user to impersonate (optional)</li>
     * </ul>
     *
     * @param params The named parameters. See description for more info.
     * @return The session
     */
    def connect(Map params) {
        def asUser = params?.asUser

        manager.clearIdentity(repository)
        manager.setIdentity(repository, newIdentity(username: username, password: password))
        def session = manager.getSession(repository)
        //println "got session [user=$session.loginUserName][repo=$session.docbaseName]"

        if(asUser) {
            def ticket = session.getLoginTicketForUser(asUser)
            def id = newIdentity(username: asUser, password: ticket)
            session.assume(id)
        }

        session
    }

    /**
     * Releases a repository session. The named parameters are:
     *
     * <ul>
     * <li>session - the session</li>
     * </ul>
     *
     * @param params The named parameters. See description for further info.
     */
    def disconnect(Map params) {
        def session = params.session
        if(session) {
            def manager = session.sessionManager
            manager.release(session)
            //println "released session"
        }
    }

    /**
     * Simple connectivity test. Usage: "SessionUtil username password repository [asUser]".
     *
     * @param args The command line parameters. See description for further info.
     */
    static main(args) {
        if(args.size() >= 3) {
            def connInfo = [username: args[0], password: args[1], repository: args[2]]
            def options = (args.size() == 4) ? [asUser: args[3]] : null

            new SessionUtil(connInfo).useSession(options) {
                println "connected to $it.docbaseName as $it.loginUserName"
            }
            println "disconnected ok"
        } else {
            println "Usage: SessionUtil username password repository [asUser]"
            System.exit(1)
        }

        System.exit(0)
    }
}
